/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package main.KBProcess;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import main.Concurrent.Iteration.IterationHelper;
import main.Concurrent.Iteration.IterationOperation;
import main.NER.Trie.ContextTrie.ORIENTATION;

/**
 *
 * @author Isaac Osesina
 */
public class DefaultKnowledgeBase implements KnowledgeBase, Serializable, Cloneable {

    //Data Members
    List<KnowledgeBaseDocument> kBDocs = new ArrayList<KnowledgeBaseDocument>();
    int KBCharCnt;
    ArrayList<Integer> kBDocsID = new ArrayList<Integer>();
    private Map<Integer, KnowledgeBaseDocument> documentIdMap = new HashMap<Integer, KnowledgeBaseDocument>();

    
    @Override
    public void addKBDoc(KnowledgeBaseDocument thisDoc) {
        if(thisDoc != null) {
            kBDocs.add(thisDoc);
            documentIdMap.put(thisDoc.getDocID(), thisDoc);
            kBDocsID.add(thisDoc.getDocID());
        }
    }
    
    @Override
    public KnowledgeBaseDocument removeKBDoc(KnowledgeBaseDocument thisDoc) {
        if(thisDoc != null) {
            kBDocs.remove(thisDoc);
            kBDocsID.remove(thisDoc.getDocID());
            return documentIdMap.remove(thisDoc.getDocID());
        }
        return null;
    }

    @Override
    public List<KnowledgeBaseDocument> removeKBDocs(List<KnowledgeBaseDocument> docs) {
        if(docs != null) {
            List<KnowledgeBaseDocument> out = new ArrayList<KnowledgeBaseDocument>();
            for(KnowledgeBaseDocument doc:docs) {
                KnowledgeBaseDocument removeKBDoc = removeKBDoc(doc);
                if(removeKBDoc != null)
                    out.add(removeKBDoc);
            }
            return out;
        }
        return null;
    }

    @Override
    public KnowledgeBaseDocument removeKBDoc(int thisDocID) {
        KnowledgeBaseDocument thisDoc = getKBDocID(thisDocID);
        if(thisDoc != null) {
            kBDocsID.remove(thisDoc.getDocID());
            kBDocs.remove(thisDoc);
            return documentIdMap.remove(thisDoc.getDocID());
        }
        return thisDoc;
    }


    @Override
    public void setKBCharCnt(int KBCharCnt) {
        this.KBCharCnt = KBCharCnt;
    }


    /**
     * Returns number of documents contained in KB
     * @return
     */
    @Override
    public int getKBSize() {
        return kBDocs.size();
    }

    /**
     * Returns the number of characters contained in KB
     * @return
     */
    @Override
    public int getKBCharCnt() {
        return KBCharCnt;
    }


    /**
     * Returns list of fragments in KB
     * @return
     */
    @Override
    public List<Fragment> getFragmentList() {
        List<Fragment> result = new ArrayList<Fragment>();
        for(KnowledgeBaseDocument doc:this.kBDocs) {
            result.addAll(doc.getFragmentList());
        }
        return result;
    }

    /**
     * Returns list of elements in KB
     * @return
     */
    @Override
    public List<Element> getElementList() {
        List<Element> result = new ArrayList<Element>();
        for(KnowledgeBaseDocument doc:this.kBDocs) {
            result.addAll(doc.getElements());
        }
        return result;
    }

    /**
     * Returns list of entities in KB
     * @return
     */
    @Override
    public List<Element> getEntityList() {
        List<Element> result = new ArrayList<Element>();
        for(KnowledgeBaseDocument doc:this.kBDocs) {
            List<Element> entities = doc.getEntities();
            if(entities != null) {
                result.addAll(entities);
            }
        }
        return result;
    }

    /**
     * Returns Document with id i
     * @param i: id of document to be returned
     * returns null if document is not contained in KB
     * @return
     */
    @Override
    public KnowledgeBaseDocument getKBDocID(int i) {
        return documentIdMap.get(i);
    }

    /**
     * Returns list of Documents contained in KB
     * @return
     */
    @Override
    public List<KnowledgeBaseDocument> getKBDocs() {
        return kBDocs;
    }

    /**
     * Returns list of document ids contained in KB
     * @return
     */
    @Override
    public List<Integer> getKBDocsID() {
        return kBDocsID;
    }

    /**
     * Returns all the texts contained in the KB documents
     * @return
     */
    @Override
    public Map<String, StringBuilder> getKBDocTextTagged() {
        Map<String, StringBuilder> docTextByType = new HashMap<String, StringBuilder>();
        for (int docNum:kBDocsID) {
            StringBuilder kBDocText = getKBDocTextTagged(docNum);
            if(kBDocText != null) {
                String documentType = this.getKBDocID(docNum).getDocType();
                StringBuilder strBuild = docTextByType.get(documentType);
                if(strBuild == null) {
                    strBuild = new StringBuilder();
                    docTextByType.put(documentType, strBuild);
                }
                strBuild.append(kBDocText);
            }
        }
        return docTextByType;
    }

    /**
     * Returns text contained in document with given id
     * @param docID: id of document
     * @return
     */
    @Override
    public StringBuilder getKBDocTextTagged(int docID) {
        KnowledgeBaseDocument thisDoc = documentIdMap.get(docID);
        if(thisDoc != null) {
            return thisDoc.getKBDocTextTagged();
        }
        return null;
    }

    @Override
    public StringBuilder getDocTextTagged_BeforeFragment(Fragment fragment){
        if(fragment != null) {
            int docID = fragment.getDocID();
            KnowledgeBaseDocument thisDoc = documentIdMap.get(docID);
            if(thisDoc != null) {
                return thisDoc.getDocTextTagged_BeforeFragment(fragment.getLeftElement());
            }
        }
        return null;
    }


    @Override
    public Fragment getFragment(int fragmentID) {
        List<Fragment> fragmentList = this.getFragmentList();
        if(fragmentList.size() > fragmentID) {
            return fragmentList.get(fragmentID);
        }
        return null;
    }

    @Override
    public List<Element> getElements_BeforeAfterFragment(Fragment fragment,  ORIENTATION orientation ){
        if(fragment == null) {
            return null;
        }
        KnowledgeBaseDocument thisDoc = documentIdMap.get(fragment.getDocID());
        if(thisDoc == null) {
            return null;
        }
        if(orientation==ORIENTATION.LEFT) {
            return new ArrayList<Element>(thisDoc.getElementsBeforeFragment(fragment.getLeftElement()));
        }
        else {
            return new ArrayList<Element>(thisDoc.getElementsAfterFragment(fragment.getRightElement()));
        }
    }


    @Override
    public int countStringOccurence(String string, ORIENTATION orientation)
    {
        int length = this.getKBSize();
        CountStringOccurrenceOperation op = new CountStringOccurrenceOperation(string, orientation);
        return IterationHelper.runIteration("CountStringOccurenceOperation", kBDocs, op, 0, length);
    }

    @Override
    public KnowledgeBase clone() {
        try {
            return (KnowledgeBase) super.clone();
        } catch (CloneNotSupportedException ex) {
            throw new InternalError();
        }
    }
    
    private class CountStringOccurrenceOperation implements IterationOperation<KnowledgeBaseDocument, Integer, Integer> {
        private String string;
        private ORIENTATION orientation;
        
        public CountStringOccurrenceOperation(String string, ORIENTATION orientation) {
            this.string = string;
            this.orientation = orientation;
        }

        @Override
        public Integer compute(int index, KnowledgeBaseDocument val) {
            return val.countStringOccurence(string, orientation);
        }

        @Override
        public Integer aggregate(Integer current, int index, Integer result) {
            return current + result;
        }
    }
}
